Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smooth ramping of ATR and Torque Tilt #2

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

surfdado
Copy link
Contributor

This feature has been tested by a handful of riders for the past few months.

Following the example of inputtilt where ramping rates of over 50deg/sec are used,
we're doing the same for ATR and Torque Tilt now.

Setpoints are ramped slower when near the current setpoint. This allows for much higher
ramping speeds without causing a jerking effect.

Instead of using 5deg/sec and 3deg/sec one can now easily run 20 and 12
or even more without any perceived downsides.

@surfdado surfdado force-pushed the smooth_ramping branch 2 times, most recently from d224d82 to 7005dda Compare June 24, 2024 05:27
surfdado added 2 commits June 23, 2024 22:27
Following the example of inputtilt, ATR setpoints are now ramped
slower when near the current setpoint. This allows for much higher
ramping speeds without causing a jerking effect.

Instead of using 5deg/sec and 3deg/sec one can now easily run 20 and 12
or even more without any perceived downsides.

Feature: Smoothened/faster setpoint ramping for ATR

Signed-off-by: Dado Mista <[email protected]>
Following the example of inputtilt, TT setpoints are now ramped
slower when near the current setpoint. This allows for much higher
ramping speeds without causing a jerking effect.

Instead of using 5deg/sec and 3deg/sec one can now easily run 20 and 12
or even more without any perceived downsides.

Feature: Smoothened/faster setpoint ramping for Torque Tilt

Signed-off-by: Dado Mista <[email protected]>
}
} else {
rate_limitf(&tt->offset, target_offset, step_size);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just having a very quick look now, I'll want this extracted into a function, not copy-pasted. I think there are also multiple ways to do the smoothing, there might be a better way that'd allow simpler (needing less context) implementation in a function. At least those were the thoughts I had when looking at the original PR...

@surfdado
Copy link
Contributor Author

Factored it out into a function now (without changing the logic). Haven't done any test rides with it yet to see if functionality got affected...

@aeraglyx
Copy link
Contributor

The way I did something similar was by having targets already smooth, multiplying target_diff by a "Responsiveness" parameter (let's say 15) to produce deg/s, which gets limited by "Max Speed". Then multiply by dt to get the step.

So just a rate limited P control. I like how simple yet user friendly it is (for example changing Max Speed doesn't affect near-target behavior). It's been working great for me, but tbf I haven't tried the proposed changes, so maybe I'm missing out on something.

@surfdado
Copy link
Contributor Author

Where are we on this? People are asking for this feature and I'm tired of sending them custom Refloat packages...

@lukash
Copy link
Owner

lukash commented Jul 31, 2024

I'm sorry for the delay, I wanted to test this, but I don't have that much experience with ATR. I'll get to it this weekend.

Copy link
Owner

@lukash lukash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this and it does feel smoother (I only really set up some decent ATR right before doing the test, I don't have a feel for it), but I'd like to get the math right.

((1 - smoothing_factor) * *ramped_step);
// Linearly ramped down step size is provided as minimum to prevent overshoot
float centering_step = fminf(fabsf(*ramped_step), fabsf(tiltback_target_diff / 2) * step) *
sign(tiltback_target_diff);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really great at math, so I may totally be missing the point, but I find this computation overly complicated and ultimately flawed.

The step here is no longer the step in itself, it's a multiplier for the tiltback_target_diff and the step is thus a linear function of the diff. The way I understand it you are filtering towards the real step (ramped_step) if outside the window and you're filtering towards half of the real step when within the window.

The line above is what I think is not working really well, consider the case when you've just entered the smooth_center_window after you've filtered almost towards step * tiltback_target_diff, so that will be the value of *ramped_step. centering_step, though, after going through the fminf(), will be fabsf(tiltback_target_diff / 2) * step, which is almost half of that, so that transition won't be smooth at all. Or am I wrong on this?

I don't dispute it's working in a way (not sure why the above non-continuity doesn't noticeably manifest), but I want the math in the package to be solid. I think this is supposed to do in essence what @aeraglyx described in #2 (comment), except his solution I think is (to the extent I can make that claim, again, I'm not really too good at math) mathematically correct. There are likely more ways of doing this too, e.g. I was thinking of maybe an exponential function (while this one is linear), etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to admit that I simply copy pasted this code without questioning it from the Input-tilt implementation. Now that you forced me to look into it I have to say that something does look fishy about that code, good job for catching that.

Before further investigating I double-checked that I didn't introduce the problem during refactoring. Thus the problem should also affect the original input tilt implementation.

I think the problem starts when ramped_step_size is updated for the smooth center window. If the smooth center window is made large enough (>> 2), it appears as if ramped_step_size could actually temporarily increase instead of decreasing, since "tiltback_target_diff / 2" can be larger than 1.

I haven't had time yet to look into a fix but I figured I owed you a response acknowledging the problem you found.

@aeraglyx
Copy link
Contributor

Very excited to see what you have in mind after reading through Discord, @lukash! I've been toying with this too, might as well share some findings:

  • What I mentioned in this comment is equivalent to applying a simple EMA filter to the target and then rate limiting it.
  • A higher order filter could work better, basically easing into sharp changes (in a way reducing setpoint acceleration) and not lingering for too long. Recently I learned that cascaded EMA filters produce some of these higher order ones (dividing alpha by the number of stages keeps roughly the same delay). 2nd order looks like a biquad with Q=0.5 (no overshoot).
  • Rate limiting before EMA can have a similar smooth quality, but it tends to add more delay, especially in the rate limited regions (compared to limiting last).
  • Personally I like defining filters that can be "felt" using half time, here's a desmos demo (slightly related to Use smooth duty universally #17). I'm pre-computing the alphas like this.

When I say "rate limiting after EMA", I'm using the EMA's output as the target of rate limiting and storing only one variable. So technically not the same as the 2 consecutive operations, but it has a smoother "release" when the limiting stops and saves resources.

Here's a plot comparing a few options on some generated noisy data.

  • Blue: 1st order -> rate limiting (updating 1 var)
  • White: 3rd order -> rate limiting (updating 3 vars)
  • Green: rate limiting -> 1st order (updating 2 vars)

filter_comparison

I'm probably risking being super annoying considering that you teased about posting plots, that's not my intention 😅

And not sure how useful it is, but I found out that this form is pretty much identical to a 2nd order IIR + rate limiting. Essentially what my previous comment suggested but with additional speed smoothing (2nd order requires 4x less filtering per "speed EMA", 3rd order 6x, not sure what's up with that). I don't have this in code, sorry.

blender_6lPdmIsb53

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants